package com.example.self_timer.timer;

import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

/**
 * Created on 2021/6/5.
 *
 * @author Alan
 * @since 1.0
 */
public class TimerManager {

    //最小单位
    private static final int TIMER_TICK = 64;
    //时间轮数量
    private static final int TIMER_LEVEL = 26;

    public HashMap<Long,TimerTask>[] timerPool;

    private ConcurrentLinkedQueue<TimerTask> inQueue = new ConcurrentLinkedQueue();

    private long newId = 0;

    public TimerManager(){
        timerPool = new HashMap[TIMER_LEVEL];
        for (int i = 0; i < TIMER_LEVEL; i ++) {
            timerPool[i] = new HashMap<>();
        }
    }

    public boolean update(long timerId,long endTime){
        //更新
        TimerTask old = del(timerId);
        if(old != null){
            old.endTime = endTime;
            add(old);
        }

        return true;
    }

    public TimerTask del(long id){
        for (int i = TIMER_TICK -1; i >= 0; i --){
            HashMap<Long,TimerTask> pool = timerPool[i];
            TimerTask task = pool.get(id);
            if (task != null) {
                pool.remove(id);
                return task;
            }
        }

        return null;
    }

    public long add(Runnable runnable, long delay){
        TimerTask task = new TimerTask();
        task.endTime = System.currentTimeMillis() + delay;
        newId += 1;
        task.id = newId;
        task.rb = runnable;

        inQueue.add(task);
        return newId;
    }

    public void add(TimerTask timerTask){
        long diff = timerTask.endTime - System.currentTimeMillis();
        if (diff <= 0){
            timerTask.rb.run(); //这里后面使用线程池
            return;
        }

        if (diff < TIMER_TICK){
            diff = TIMER_TICK;
        }

        for (int i = TIMER_LEVEL - 1;i >=0;i--){
            if (diff >= ((long) TIMER_TICK << i)){
                timerPool[i].put(timerTask.id, timerTask);
                break;
            }
        }
    }

    public void go(){
        Thread thread = new Thread(this::run);
        thread.start();
    }

    private void run(){
        //开启一个定时器，间隔时间执行
        long lastTick = System.currentTimeMillis() / TIMER_TICK;

        while (true){
            try {
                TimerTask timerTask = inQueue.poll();
                if(timerTask != null){
                    add(timerTask);
                }else {
                    lastTick = doTick(lastTick);
                    Thread.sleep(TIMER_TICK);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private long doTick(long lastTick){
        long newTick = System.currentTimeMillis() / TIMER_TICK;
        if (newTick < lastTick){
            return newTick;
        }

        do {
            lastTick++;
            for (int i = TIMER_LEVEL - 1; i >= 0; i--) {
                long mask = 1L << i - 1;
                if ((lastTick & mask) == 0) {
                    trigger(i);
                }
            }

        } while (lastTick < newTick);
        return newTick;
    }

    private void trigger(int level){
        long newTick = System.currentTimeMillis();
        HashMap<Long,TimerTask> lkotMap = timerPool[level];

        List<Long> list = new LinkedList<>();

        try {
            for (TimerTask task : lkotMap.values()) {
                if (task.endTime - newTick < (1L << level) * TIMER_LEVEL){
                    if(level != 0){
                        timerPool[level - 1].put(task.id, task);
                        list.add(task.id);
                    }else if (newTick > task.endTime){
                        task.rb.run(); //后面使用线程池
                        list.add(task.id);
                    }
                }
            }
            for (long key : list) {
                lkotMap.remove(key);
            }
        }catch (Exception e){
            e.printStackTrace();
        }

    }
}
